#!/usr/bin/env python
#coding=utf-8

import json, qiniu, uuid, os, pili, hashlib, requests
from urlparse import urlparse, urlunparse

from flask import current_app, request
from werkzeug import FileStorage

from codingabc import code
from codingabc.database import db
from codingabc.helpers import log_debug, log_info, log_error, toint, get_count

from app.helpers.date_time import current_timestamp, timestamp2str
import hmac
import hashlib
import base64


class QiniuService(object):
    """ 七牛Service """

    def __init__(self):
        self.msg                = u''
        self.access_key         = current_app.config.get('ACCESS_KEY')
        self.secret_key         = current_app.config.get('SECRET_KEY')
        self.static_bucket_name = current_app.config.get('BUCKET_NAME')
        self.static_cdn_domain  = current_app.config.get('STATIC_CDN_DOMAIN')
        self.current_time       = current_timestamp()

        self.image_mime_type_list = ['image/jpeg', 'image/pjpeg', 'image/png']
        self.video_mime_type_list = ['video/mp4']
        self.all_mime_type_list   = self.image_mime_type_list + self.video_mime_type_list

        # 初始化Auth状态
        self.q = qiniu.Auth(self.access_key, self.secret_key)

        # 初始化BucketManager
        self.bucket = qiniu.BucketManager(self.q)


class QiniuUploadService(QiniuService):
    """ 七牛上传Service """

    def __init__(self, storage, category='default', filetype='image'):
        super(QiniuUploadService, self).__init__()

        self.storage     = storage
        self.category    = category
        self.filetype    = filetype
        self.url         = u''
        self.key         = u''
        self.token       = u''
        self.file_path   = u''

    def create_key(self):
        """ 创建key """

        # 扩展名
        try:
            filename  = self.storage.filename
            name, ext = os.path.splitext(filename)
            ext_name  = ext.strip('.')
        except Exception, e:
            log_info('[InfoQiniuUploadServiceCreateKey][ExtNameError] storage.filename:%s  e:%s' % (self.storage.filename, e))
            return False

        # 4c859fc5-6b33-426d-b81a-6a133b6327e9
        _uuid = uuid.uuid4()

        # default/2017-03-18/
        date_str    = timestamp2str(self.current_time)
        folder_path = u'%s/%s/' % (self.category, date_str)

        # /default/2017-03-18/4c859fc5-6b33-426d-b81a-6a133b6327e9.jpg
        self.key = u'%s%s.%s' % (folder_path, _uuid, ext_name.lower())

        return True

    def save_storage(self):
        """ 保存storage到本地 """

        # /data/www/upload/
        save_target_path = current_app.config.get('SAVE_TARGET_PATH')

        # /data/www/upload/default/2017-03-18/4c859fc5-6b33-426d-b81a-6a133b6327e9.jpg
        self.file_path = '%s%s' % (save_target_path, self.key.strip('/'))

        # 创建目录
        dirname = os.path.dirname(self.file_path)
        try:
            if not os.path.exists(dirname):
                os.makedirs(dirname)
        except Exception, e:
            log_info('[InfoQiniuUploadServiceSaveStorage][MakeDirsError] file_path:%s  e:%s' % (self.file_path, e))
            return False

        # 保存文件
        try:
            self.storage.save(self.file_path)
        except Exception, e:
            log_info('[InfoQiniuUploadServiceSaveStorage][SaveError] file_path:%s  e:%s' % (self.file_path, e))
            return False

        return True

    def check(self):
        """ 检查 """

        # 检查 - 是否storage对象
        if not isinstance(self.storage, FileStorage):
            self.msg = u'storage must be a werkzeug.FileStorage'
            return False

        # 检查 - mime_type
        mime_type = self.storage.content_type
        if self.filetype == 'image':
            if mime_type not in self.image_mime_type_list:
                self.msg = u'不允许的文件类型'
                return False
        elif self.filetype == 'video':
            if mime_type not in self.video_mime_type_list:
                self.msg = u'不允许的文件类型'
                return False
        else:
            if mime_type not in self.all_mime_type_list:
                self.msg = u'不允许的文件类型'
                return False

        # 创建key
        if not self.create_key():
            self.msg = u'创建key失败'
            return False

        # 保存storage到本地
        if not self.save_storage():
            self.msg = u'保存上传文件失败'
            return False

        # 获取上传token
        self.token = QiniuStaticMethodsService.get_upload_token(self.key)

        return True

    def upload(self):
        """ 上传 """

        log_info('[InfoQiniuUploadServiceUpload][UploadInfo] token:%s  key:%s  file_path:%s' % (self.token, self.key, self.file_path))

        # 上传
        ret, info = qiniu.put_file(self.token, self.key, self.file_path)

        if info.status_code == 200:
            text_body = json.loads(info.text_body)
            self.key  = text_body.get('key', '')
            if not self.key:
                log_info('[InfoQiniuUploadServiceUpload][UploadError] token:%s  key:%s  file_path:%s  info:%s' %\
                            (self.token, self.key, self.file_path, info))
                self.msg = u'上传失败'
                return False
        else:
            log_info('[InfoQiniuUploadServiceUpload][UploadError] token:%s  key:%s  file_path:%s  info:%s' %\
                        (self.token, self.key, self.file_path, info))
            self.msg = u'上传失败'
            return False

        if self.msg:
            return False

        self.url = '%s%s' % (self.static_cdn_domain, self.key)

        return True


class QiniuStaticMethodsService(object):
    """ 七牛静态方法Service """

    @staticmethod
    def get_upload_token(key):
        """ 获取上传token """

        qs = QiniuService()

        token = qs.q.upload_token(qs.static_bucket_name, key, 3600)

        return token

    @staticmethod
    def check_file(key, filetype='image'):
        """ 检查文件 """

        url = u''
        qs  = QiniuService()

        # 根据key获取文件信息
        ret, info = qs.bucket.stat(qs.static_bucket_name, key)
        log_info('[InfoServicesQiniuStaticMethodsServiceCheckfile] key:%s  info:%s' % (key, info))

        # 获取不到文件信息
        if info.status_code != 200:
            log_info('[InfoServicesQiniuStaticMethodsServiceCheckfile][FileError] key:%s  info:%s' % (key, info))
            return (False, u'上传文件错误', url)

        text_body = json.loads(info.text_body)
        mime_type = text_body.get('mimeType', None)
        # 判断文件类型
        if filetype == 'image':
            # 是否允许的图片类型
            if mime_type not in qs.image_mime_type_list:
                return (False, u'上传文件必须是图片', url)
        # 判断文件类型
        if filetype == 'video':
            # 是否允许的图片类型
            if mime_type not in qs.video_mime_type_list:
                return (False, u'上传文件必须mp4格式', url)
        # 是否允许文件类型
        if mime_type not in qs.all_mime_type_list:
            return (False, u'非法的上传文件', url)

        url = '%s%s' % (qs.static_cdn_domain, key)

        return (True, u'', url)


class QiniuLiveStaticMethodsService(object):
    """ 七牛直播静态方法Service """

    @staticmethod
    def hmac_sha1(data, key):
        """ hmac_sha1 """

        hashed = hmac.new(key, data, hashlib.sha1)

        return base64.urlsafe_b64encode(hashed.digest())

    @staticmethod
    def get_token(url, content, body=None, method=None):
        """ 获取上传token """

        access_key   = current_app.config.get('ACCESS_KEY')
        secret_key   = current_app.config.get('SECRET_KEY')

        # parsed
        parsed = urlparse(url)

        # pathdir
        pathdir = method + " " + parsed.path
        if parsed.query != "":
            pathdir = pathdir + "?" + parsed.query

        # pathdir_content_type
        pathdir_host          = pathdir + "\nHost: " + parsed.netloc + "\n"
        pathdir_content_type  = pathdir_host + content + "\n\n"
        if body != None:
            pathdir_content_type = pathdir_content_type + body

        # auth_data
        auth_data = QiniuLiveStaticMethodsService.hmac_sha1(pathdir_content_type, secret_key)

        token = "Qiniu " + access_key + ":" + auth_data

        return token

    @staticmethod
    def get_auth_url(url, expired_time):
        """ 获取时间戳防盗链鉴权签名(tsExpireMD5)url """
        _url = u''
        sss  = u'[InfoServicesQiniuLiveStaticMethodsServiceGetAuthSign]'

        # 获取url的path
        try:
            parsed    = urlparse(url)
            path      = parsed.path
        except Exception, e:
            log_info('%s[PathError] url:%s  expired_time:%s' % (sss, url, expired_time))
            return _url

        # 转换过期时间: 十进制转换为十六进制
        try:
            time = hex(expired_time)[2:]
        except Exception, e:
            log_info('%s[TimeError] url:%s  expired_time:%s' % (sss, url, expired_time))
            return _url

        # 获取key
        key = current_app.config.get('QINIU_LIVE_AUTH_KEY', '')
        if not key:
            log_info('%s[KeyError] url:%s  expired_time:%s' % (sss, url, expired_time))
            return _url

        # 创建签名
        _str = key + path + time
        sign = hashlib.md5(_str).hexdigest()
        sign = sign.lower()

        # 重新组装url
        try:
            parsed4 = '%s&' % parsed[4] if parsed[4] else ''
            query   = '%ssign=%s&t=%s' % (parsed4, sign, time)
            _parsed = (parsed[0], parsed[1], parsed[2], parsed[3], query, parsed[5])
            _url    = urlunparse(_parsed)
        except Exception, e:
            log_info('%s[CreateError] url:%s  expired_time:%s' % (sss, url, expired_time))
            return _url

        return _url

    @staticmethod
    def create_rtmp_publish_url(stream_title='', expire=0):
        """ 创建推流地址 """

        access_key       = current_app.config.get('ACCESS_KEY', '')
        secret_key       = current_app.config.get('SECRET_KEY', '')
        hub_name         = current_app.config.get('QINIU_LIVE_HUB_NAME', '')
        publish_domain   = current_app.config.get('QINIU_LIVE_PUBLISH_DOMAIN', '')
        rtmp_publish_url = ''

        if not stream_title:
            return rtmp_publish_url

        # 创建mac
        mac = pili.Mac(access_key, secret_key)

        # 创建推流地址
        rtmp_publish_url = pili.rtmp_publish_url(publish_domain, hub_name, stream_title, mac, expire)

        return rtmp_publish_url

    @staticmethod
    def stream_saveas(stream_title='', start_time=0, current_time=0):
        """ 保存流 """

        access_key     = current_app.config.get('ACCESS_KEY', '')
        secret_key     = current_app.config.get('SECRET_KEY', '')
        bucket_name    = current_app.config.get('QINIU_LIVE_BUCKET_NAME', '')
        hub_name       = current_app.config.get('QINIU_LIVE_HUB_NAME', '')
        publish_domain = current_app.config.get('QINIU_LIVE_PUBLISH_DOMAIN', '')
        cdn_domain     = current_app.config.get('QINIU_LIVE_CDN_DOMAIN', '')

        rtmp_playback_url = ''
        sss = u'[InfoServicesQiniuLiveStaticMethodsServiceStreamSaveas]'

        if not stream_title:
            return rtmp_playback_url

        if start_time <= 0:
            return rtmp_playback_url

        # 实例化Hub对象
        mac    = pili.Mac(access_key, secret_key)
        client = pili.Client(mac)
        hub    = client.hub(hub_name)

        # 流
        stream = hub.get(stream_title)

        # 文件名称
        current_time = current_time if current_time else current_timestamp()
        fname        = '%s_%s' % (stream_title, current_time)

        # 获取流历史活动
        try:
            history_list = stream.history(start_time)
        except Exception, e:
            return rtmp_playback_url

        # 检查 - 获取流历史活动
        if len(history_list) == 0:
            log_info('%s[HistoryListEmpty] stream_title:%s  start_time:%s' % (sss, stream_title, start_time))
            return rtmp_playback_url

        # 获取回放开始时间
        start = 9999999999
        end   = 0
        for history in history_list:
            _start = toint(history['start'])
            _end   = toint(history['end'])
            start  = _start if start > _start else start
            end    = _end if end < _end else end

        # 保存回放
        try:
            stream.saveas(start_second=start, fname=fname)
        except Exception, e:
            log_info('%s[SaveStreamError] stream_title:%s  start_time:%s  e:%s' % (sss, stream_title, start_time, e))
            return rtmp_playback_url

        # RTMP回放地址
        rtmp_playback_url = '%s%s.m3u8' % (cdn_domain, fname)

        return rtmp_playback_url

    @staticmethod
    def online_count(key):
        """ 统计直播在线人数 """

        # 在线人数
        online_count = 0

        hub_name = current_app.config.get('QINIU_LIVE_HUB_NAME', '')
        url      = 'http://pili.qiniuapi.com/v2/hubs/%s/stat/play' % hub_name
        content  = 'Content-Type: application/json'
        method   = 'GET'
        token    = QiniuLiveStaticMethodsService.get_token(url, content, body=None, method=method)

        # 在线人数 - 头信息
        headers = {'Authorization':str(token), 'User-Agent':'Python-urllib/2.7', 'Content-Type':'application/json'}

        # 在线人数 - 获取在线人数
        res = requests.get(url, headers=headers)
        if res.status_code == 200:
            ret = res.json()
            streams = ret.get('streams', {})
            if streams:
                stream = streams.get(key, {})
                if stream:
                    online_count = stream.get('count', 0)

        return online_count




